#pragma once

#include <iostream>
#include <string>
#include <cerrno>
#include <functional>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

namespace Server
{
    using namespace std;

    // using func_t = function<void (int, string, uint16_t, string)>;
    typedef function<void (int, string, uint16_t, string)> func_t;

    static const std::string defaultIp = "0.0.0.0";
    static const int gnum = 1024;

    enum {USAGE_ERR = 1, SOCK_ERR, BIND_ERR, OPEN_ERR};

    class udpServer
    {
    public:
        udpServer(const func_t cb, const uint16_t &port, const std::string ip = defaultIp)
        : _callback(cb), _port(port), _ip(ip), _sockfd(-1)
        {}
        void initServer()
        {
            _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
            if(_sockfd == -1)
            {
                std::cerr << "socket error" << errno << " : " << strerror(errno) << std::endl;
                exit(SOCK_ERR);
            }
            cout << "socket success: " << _sockfd << endl;

            // 2. 绑定port, ip(TODO)
            struct sockaddr_in local; // 定义一个变量
            bzero(&local, sizeof local);
            local.sin_family = AF_INET;
            local.sin_port = htons(_port); // 要考虑port的大小端问题
            local.sin_addr.s_addr = inet_addr(_ip.c_str()); // 1. string -> unit32_t  2. htonl();
            int n = bind(_sockfd, (struct sockaddr*)&local, sizeof local);
            if(n == -1)
            {
                std::cerr << "bind error" << errno << " : " << strerror(errno) << std::endl;
                exit(BIND_ERR);
            }
        }
        void start()
        {
            char buffer[gnum];
            for(;;)
            {
                struct sockaddr_in peer;
                socklen_t len = sizeof peer; // 必填
                ssize_t s = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&peer, &len);
                // 1. 数据是什么
                if(s > 0)
                {
                    buffer[s] = 0;
                    string clientip = inet_ntoa(peer.sin_addr);
                    uint16_t clientport = ntohs(peer.sin_port);
                    string message = buffer;

                    cout << clientip << "[" << clientport << "]# " << message << endl;
                    _callback(_sockfd, clientip, clientport, message);
                }
            }
        }
        ~udpServer()
        {}
    private:
        uint16_t _port;
        std::string _ip; // 服务器应接受所有的报文, 不建议绑定指定的IP
        int _sockfd;
        func_t _callback;
    };
}